//
//  KSSlideMenuView.swift
//  KSSlideMenuView
//
//  Created by Zero on 2017/9/18.
//  Copyright © 2017年 yangzhilan. All rights reserved.
//

import UIKit

/// Controller切换
class KSSlideMenuView: UIView {
    var isFixed:Bool = false                    // 是否是固定型菜单（不需要修正滚动）
    var lazyLoad:Bool = true                    // 是否懒加载子控制器
    var selectedColor:UIColor = UIColor.red {   // 选中的颜色
        didSet {
            for item in itemButtons {
                item.setTitleColor(selectedColor, for: .selected)
            }
        }
    }
    var unSelectedColor:UIColor = UIColor.black {   // 未选中的颜色
        didSet {
            for item in itemButtons {
                item.setTitleColor(unSelectedColor, for: .normal)
            }
        }
    }
    var indicatorWidth:CGFloat = 30 {   // 下标宽度
        didSet {
            setNeedsLayout()
        }
    }
    var indicatorHeight:CGFloat = 2 {   // 下标高度
        didSet {
            setNeedsLayout()
        }
    }
    var bottomPadding:CGFloat = 0 {     // 下标距离底部距离
        didSet {
            setNeedsLayout()
        }
    }
    var font:UIFont = UIFont.systemFont(ofSize: 13) {   // 标题字体
        didSet {
            needLayout = true
            setNeedsLayout()
        }
    }
    weak var bodySuperView:UIView? {            // bodyScrollView的父视图,默认为SlideMenu的父视图
        didSet{
            needLayout = true
            setNeedsLayout()
        }
    }
    var bodyFrame:CGRect = CGRect.zero {        // bodyScrollView的frame
        didSet{
            bodyScrollView.frame = bodyFrame
        }
    }
    lazy var tabScrollView:UIScrollView = {     // 菜单栏
        let  tabScrollView = UIScrollView.init(frame: self.bounds)
        tabScrollView.showsVerticalScrollIndicator = false
        tabScrollView.showsHorizontalScrollIndicator = false
        tabScrollView.backgroundColor = UIColor.clear
        return tabScrollView
    }()
    
    lazy var bodyScrollView:UIScrollView = {    // 内容视图
        let bodyScrollView = UIScrollView.init(frame: CGRect.zero)
        bodyScrollView.showsVerticalScrollIndicator = false
        bodyScrollView.showsHorizontalScrollIndicator = false
        bodyScrollView.isPagingEnabled = true
        bodyScrollView.bounces = false
        bodyScrollView.delegate = self
        return bodyScrollView
    }()
    lazy var indicatorView:UIView = UIView()    // 下标视图
    fileprivate(set) var currentIndex:Int = 0   // 当前索引
    lazy var line:UIView = UIView()             // 底部分割线 默认不显示
    fileprivate var leftIndex:Int = 0
    fileprivate var rightIndex:Int = 0
    fileprivate var itemButtons:Array<UIButton>  = []
    fileprivate var itemMargin:CGFloat = 15.0   // tab文字的边距
    private var needLayout:Bool = true
    private var titlesArr:Array<String>
    private var controllers:Array<UIViewController>
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    init(frame:CGRect,titles:Array<String>, childControllers:Array<UIViewController>) {
        titlesArr = titles
        controllers = childControllers
        super.init(frame: frame)
        
        addSubview(tabScrollView)
        tabScrollView.addSubview(indicatorView)
        indicatorView.backgroundColor = selectedColor
        line.backgroundColor = UIColor(white: 0.9, alpha: 1)
        line.isHidden = true
        self.addSubview(line)
    }
    
    /// 布局
    override func layoutSubviews() {
        super.layoutSubviews()
        tabScrollView.frame = self.bounds
        line.frame = CGRect(x: 0, y: self.frame.height - 0.5, width: self.frame.width, height: 0.5)
        if needLayout {
            for item in itemButtons {
                item.removeFromSuperview()
            }
            itemButtons.removeAll()
            setupTabScrollView()
            if bodySuperView != nil {
                bodySuperView?.addSubview(bodyScrollView)
            } else {
                superview?.addSubview(bodyScrollView)
            }
            if lazyLoad {
                lazyLoadContents(at: currentIndex)
            } else {
                for (index, vc) in controllers.enumerated() {
                    vc.view.frame = bodyScrollView.bounds
                    vc.view.center = CGPoint(x: bodyScrollView.frame.width*(CGFloat(index)+0.5), y: bodyScrollView.frame.height/2)
                    bodyScrollView.addSubview(vc.view)
                }
            }
            bodyScrollView.contentSize = CGSize(width: bodyScrollView.frame.width*CGFloat(controllers.count), height: bodyScrollView.frame.height)
            bodyScrollView.setContentOffset(CGPoint(x:bodyScrollView.frame.size.width*CGFloat(currentIndex), y:0), animated: false)
            resetTabScrollViewFrame()
            needLayout = false
        }
        setupIndicatorView()
    }
    
    /// 设置导航栏
    private func setupTabScrollView()  {
        var originX:CGFloat = 0
        var totalTextLenght:CGFloat = 0
        for (index,title) in titlesArr.enumerated() {
            let item = UIButton.init()
            item.setTitleColor(selectedColor, for: .selected)
            item.setTitleColor(unSelectedColor, for: .normal)
            item.reversesTitleShadowWhenHighlighted = true
            item.titleLabel?.font = font
            item.setTitle(title, for: .normal)
            item.addTarget(self, action: #selector(itemClicked(_:)), for: .touchUpInside)
    
            let size = (title as NSString).size(withAttributes: [NSAttributedStringKey.fontNSAttributedStringKey.font:font])
            item.frame = CGRect(x: originX, y: 0, width: size.width + itemMargin*2, height: self.frame.height)
            item.textWidth = size.width
            originX = item.frame.maxX
            itemButtons.append(item)
            tabScrollView.addSubview(item)
            item.isSelected = index == currentIndex
            if index == currentIndex {
                transformItem(item, isIdentify: false)
            }
            totalTextLenght += size.width
        }
        
        /// 修改frame
        if isFixed {
            if totalTextLenght > self.frame.width {
                // 文字长度超过容器，则平分宽度
                let width = self.frame.width/CGFloat(self.titlesArr.count)
                for (index,item) in itemButtons.enumerated() {
                    item.frame = CGRect(x: width*CGFloat(index), y: 0, width: width, height: item.frame.height)
                    item.textWidth = width
                }
            } else {
                // 未超过，按照文本长度计算margin
                let margin = (self.frame.width - totalTextLenght)/CGFloat(titlesArr.count*2)
                originX = 0
                for (_,item) in itemButtons.enumerated() {
                    item.frame = CGRect(x: originX, y: 0, width: item.textWidth+2*margin, height: item.frame.height)
                    originX = item.frame.maxX
                }
            }
        }
        tabScrollView.contentSize = CGSize(width: originX, height: self.frame.height)
    }

    // 配置下标
    private func setupIndicatorView() {
        var frame = itemButtons[currentIndex].frame
        frame.origin.y = self.frame.height - bottomPadding - indicatorHeight
        frame.size.height = indicatorHeight

        let text = titlesArr[currentIndex]
        let size = (text as NSString).size(withAttributes: [NSAttributedStringKey.font:font])
        frame.origin.x = frame.midX - size.width/2
        frame.size.width = size.width
        indicatorView.frame = frame
    }
    
    // 修正tabScrollView的位置
    fileprivate func resetTabScrollViewFrame() {
        if self.isFixed {
            return
        }
        let seletedItem = itemButtons[currentIndex]
        let tab_width = tabScrollView.frame.width
        var reviseX:CGFloat = 0
        
        if seletedItem.center.x + tab_width/2 >= tabScrollView.contentSize.width {
            reviseX = tabScrollView.contentSize.width - tab_width
        } else if (seletedItem.center.x - tab_width/2) <= 0 {
            reviseX = 0
        } else {
            reviseX = seletedItem.center.x - tab_width/2
        }
        tabScrollView.setContentOffset(CGPoint(x:reviseX,y:0), animated: true)
    }
    
    /// 懒加载子控制器
    ///
    /// - Parameter index: 索引
    fileprivate func lazyLoadContents(at index:Int)  {
        let vc = controllers[index]
        if !vc.isViewLoaded {
            vc.view.frame = bodyScrollView.bounds
            vc.view.center = CGPoint(x: bodyScrollView.frame.width*(CGFloat(index)+0.5), y: bodyScrollView.frame.height/2)
            bodyScrollView.addSubview(vc.view)
        }
    }
    
    deinit {
        print("\(#function)")
    }
    
    /// MARK: - 事件及UI
    func scrollToIndex(_ index:UInt) {
        if itemButtons.count > Int(index) {
            let button = itemButtons[currentIndex]
            itemClicked(button)
        } else {
            currentIndex = Int(index)
        }
    }
    
    /// 更新下标的UI效果
    ///
    /// - Parameter relativeLacation: 滑动的相对距离
    func updateIndicatorStyle(_ relativeLacation:CGFloat) {
        let leftItem = itemButtons[leftIndex]
        let rightItem = itemButtons[rightIndex]
        
        var frame = self.indicatorView.frame
        let maxWidth = rightItem.textWidth - leftItem.textWidth
        frame.size.width = leftItem.textWidth + maxWidth*relativeLacation
        indicatorView.frame = frame
            
        let max = rightItem.center.x - leftItem.center.x
        indicatorView.center = CGPoint(x:leftItem.center.x + max*relativeLacation,y:indicatorView.center.y)
    }
    
    /// 更新标题的UI效果
    ///
    /// - Parameter relativeLacation: 滑动的相对距离
    func updateTitleStyle(_ relativeLacation:CGFloat) {
        
        let leftItem = itemButtons[leftIndex]
        let rightItem = itemButtons[rightIndex]
        
        leftItem.isSelected = relativeLacation <= 0.5
        rightItem.isSelected = relativeLacation > 0.5
        if relativeLacation <= 0.5 {
            transformItem(leftItem, isIdentify: false)
            transformItem(rightItem, isIdentify: true)
        } else {
            transformItem(leftItem, isIdentify: true)
            transformItem(rightItem, isIdentify: false)
        }
    }
    
    /// 对按钮进行放大和还原
    private func transformItem(_ item:UIButton, isIdentify:Bool) {
        item.isSelected = !isIdentify
        item.transform = isIdentify ? CGAffineTransform.identity : CGAffineTransform.init(scaleX: 1.05, y: 1.05)
    }
    
    /// 菜单按钮点击事件
    @objc func itemClicked(_ button:UIButton) {
        if button.isSelected {
            return
        }
        let fromIndex = currentIndex
        currentIndex = itemButtons.index(of: button)!
        self.changeTitleItem(from: fromIndex, to: currentIndex)
        self.changeIndicator(from: fromIndex, to: currentIndex)
        bodyScrollView.setContentOffset(CGPoint(x:bodyScrollView.frame.size.width*CGFloat(currentIndex), y:0), animated: false)
        print(currentIndex)
        self.resetTabScrollViewFrame()
    }
    
    /// 点击事件触发的UI更新
    private func changeTitleItem(from:Int ,to:Int) {
        self.itemButtons[from].isSelected = false
        self.itemButtons[to].isSelected = true
        
        UIView.animate(withDuration: 0.25, animations: {
        self.itemButtons[to].transform = CGAffineTransform.init(scaleX: 1.05, y: 1.05)
        self.itemButtons[from].transform = CGAffineTransform.identity
                
        }, completion: nil)
    }
    
    /// 点击事件触发的UI更新
    private func changeIndicator(from:Int, to:Int) {
        let toItem = itemButtons[to]
            var bounds = indicatorView.bounds
            bounds.size.width = toItem.textWidth
            UIView.animate(withDuration: 0.3, animations: {
                self.indicatorView.bounds = bounds
                self.indicatorView.center = CGPoint(x:toItem.center.x,y:self.indicatorView.center.y)
            })
        }
    }

/// MARK: - ScrollView的代理
extension KSSlideMenuView: UIScrollViewDelegate {
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView == self.bodyScrollView {
            let offset = scrollView.contentOffset
            currentIndex = lroundf(Float(scrollView.contentOffset.x / scrollView.frame.size.width))
            
            var index = currentIndex
            if offset.x > scrollView.frame.width*CGFloat(currentIndex) {
                index = (currentIndex + 1) >= itemButtons.count ? currentIndex : currentIndex + 1
            } else if (offset.x < scrollView.frame.width*CGFloat(currentIndex)) {
                index = (currentIndex - 1) < 0 ? 0 : currentIndex - 1
            }
            lazyLoadContents(at:index)
            if offset.x <= 0 {
                print("左滑")
                // 左边界
                leftIndex = 0
                rightIndex = leftIndex
            } else if (offset.x >= scrollView.contentSize.width - scrollView.frame.width) {
                print("右滑")
                //右边界
                leftIndex = itemButtons.count - 1
                rightIndex = leftIndex
            } else {
                leftIndex = Int(offset.x/scrollView.frame.width)
                rightIndex = leftIndex + 1
            }
            
            // 计算偏移的相对位移
            let relativeLacation = bodyScrollView.contentOffset.x/bodyScrollView.frame.width - CGFloat(leftIndex)
            if relativeLacation == 0 {
                return
            }
            // 更新UI
            self.updateIndicatorStyle(relativeLacation)
            self.updateTitleStyle(relativeLacation)
            
        }
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if scrollView == bodyScrollView {
            currentIndex = Int(scrollView.contentOffset.x / scrollView.frame.size.width)
            self.resetTabScrollViewFrame()
        }
    }
}

private extension UIButton {
    private struct AssociatedKeys {
        static var textWidthName = "key_textWidth"
    }
    /// title的文本长度
    var textWidth:CGFloat {
        get{
            return objc_getAssociatedObject(self, &AssociatedKeys.textWidthName) as? CGFloat ?? 0
        }
        set{
            objc_setAssociatedObject(self, &AssociatedKeys.textWidthName, newValue , objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    var textMinX:CGFloat {
        get {
            return (self.frame.width - self.textWidth)/2 + self.frame.minX
        }
    }
    var textMaxX:CGFloat {
        get {
            return self.frame.maxX - (self.frame.width - self.textWidth)/2
        }
    }
}



